package com.gitease.quickhub.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitease.quickhub.model.Commit;
import com.gitease.quickhub.model.Repository;
import com.gitease.quickhub.model.RepositoryFile;
import com.gitease.quickhub.model.User;
import com.gitease.quickhub.service.GiteeApiService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class GiteeApiServiceImpl implements GiteeApiService {

    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;

    @Value("${gitee.client.id}")
    private String clientId;

    @Value("${gitee.client.secret}")
    private String clientSecret;

    @Value("${gitee.redirect.uri}")
    private String redirectUri;

    @Value("${gitee.token.url}")
    private String tokenUrl;

    @Value("${gitee.api.url}")
    private String apiUrl;

    public GiteeApiServiceImpl(ObjectMapper objectMapper) {
        this.restTemplate = new RestTemplate();
        this.objectMapper = objectMapper;
    }

    @Override
    public User getUserInfo(String accessToken) {
        String url = apiUrl + "/user";
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        try {
            return objectMapper.readValue(response.getBody(), User.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing user info", e);
        }
    }

    @Override
    public List<Repository> getUserRepositories(String accessToken) {
        String url = apiUrl + "/user/repos";
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        try {
            // 先将响应解析为 RepositoryDTO 列表
            List<com.gitease.quickhub.dto.RepositoryDTO> repositoryDTOs = objectMapper.readValue(
                    response.getBody(),
                    new TypeReference<List<com.gitease.quickhub.dto.RepositoryDTO>>() {
                    });

            // 将 RepositoryDTO 转换为 Repository 实体
            return repositoryDTOs.stream().map(dto -> {
                Repository repository = new Repository();
                repository.setId(dto.getId());
                repository.setName(dto.getName());
                repository.setFullName(dto.getFullName());
                repository.setDescription(dto.getDescription());
                // 清理 htmlUrl 中的多余空格和反引号
                String htmlUrl = dto.getHtmlUrl();
                if (htmlUrl != null) {
                    htmlUrl = htmlUrl.trim().replace("`", "");
                }
                repository.setHtmlUrl(htmlUrl);
                repository.setSshUrl(dto.getSshUrl());
                repository.setCloneUrl(dto.getCloneUrl());
                repository.setDefaultBranch(dto.getDefaultBranch());
                repository.setPrivate(dto.isPrivate());
                repository.setFork(dto.isFork());
                repository.setStargazersCount(dto.getStargazersCount());
                repository.setWatchersCount(dto.getWatchersCount());
                repository.setForksCount(dto.getForksCount());
                repository.setOpenIssuesCount(dto.getOpenIssuesCount());
                repository.setCreatedAt(dto.getCreatedAt());
                repository.setUpdatedAt(dto.getUpdatedAt());
                repository.setPushedAt(dto.getPushedAt());

                // 设置所有者ID
                if (dto.getOwner() != null) {
                    repository.setOwnerId(dto.getOwner().getId());
                }

                return repository;
            }).collect(Collectors.toList());
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing repositories", e);
        }
    }

    @Override
    public Repository getRepository(String accessToken, String owner, String repo) {
        String url = apiUrl + "/repos/" + owner + "/" + repo;
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        try {
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
            try {
                // 先将响应解析为 RepositoryDTO
                com.gitease.quickhub.dto.RepositoryDTO dto = objectMapper.readValue(
                        response.getBody(),
                        com.gitease.quickhub.dto.RepositoryDTO.class);

                // 将 RepositoryDTO 转换为 Repository 实体
                Repository repository = new Repository();
                repository.setId(dto.getId());
                repository.setName(dto.getName());
                repository.setFullName(dto.getFullName());
                repository.setDescription(dto.getDescription());
                // 清理 htmlUrl 中的多余空格和反引号
                String htmlUrl = dto.getHtmlUrl();
                if (htmlUrl != null) {
                    htmlUrl = htmlUrl.trim().replace("`", "");
                }
                repository.setHtmlUrl(htmlUrl);
                repository.setSshUrl(dto.getSshUrl());
                repository.setCloneUrl(dto.getCloneUrl());
                repository.setDefaultBranch(dto.getDefaultBranch());
                repository.setPrivate(dto.isPrivate());
                repository.setFork(dto.isFork());
                repository.setStargazersCount(dto.getStargazersCount());
                repository.setWatchersCount(dto.getWatchersCount());
                repository.setForksCount(dto.getForksCount());
                repository.setOpenIssuesCount(dto.getOpenIssuesCount());
                repository.setCreatedAt(dto.getCreatedAt());
                repository.setUpdatedAt(dto.getUpdatedAt());
                repository.setPushedAt(dto.getPushedAt());

                // 设置所有者ID
                if (dto.getOwner() != null) {
                    repository.setOwnerId(dto.getOwner().getId());
                }

                return repository;
            } catch (JsonProcessingException e) {
                throw new RuntimeException("Error parsing repository", e);
            }
        } catch (org.springframework.web.client.HttpClientErrorException.NotFound e) {
            throw new RuntimeException("Repository not found: " + owner + "/" + repo, e);
        } catch (org.springframework.web.client.HttpClientErrorException e) {
            throw new RuntimeException("Error accessing repository: " + e.getStatusCode() + " " + e.getStatusText(), e);
        }
    }

    @Override
    public List<RepositoryFile> getRepositoryContents(String accessToken, String owner, String repo, String path,
            String ref) {
        // 构建基础URL，避免双斜杠问题
        String url;
        if (path == null || path.isEmpty()) {
            url = apiUrl + "/repos/" + owner + "/" + repo + "/contents";
        } else {
            url = apiUrl + "/repos/" + owner + "/" + repo + "/contents/" + path;
        }
        
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        if (ref != null && !ref.isEmpty()) {
            builder.queryParam("ref", ref);
        }

        ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity,
                String.class);
        try {
            return objectMapper.readValue(response.getBody(), new TypeReference<List<RepositoryFile>>() {
            });
        } catch (JsonProcessingException e) {
            try {
                // If it's a single file, it won't be returned as an array
                RepositoryFile file = objectMapper.readValue(response.getBody(), RepositoryFile.class);
                return List.of(file);
            } catch (JsonProcessingException ex) {
                throw new RuntimeException("Error parsing repository contents", ex);
            }
        }
    }

    @Override
    public RepositoryFile getRepositoryFile(String accessToken, String owner, String repo, String path, String ref) {
        // 确保路径参数格式正确
        if (path == null || path.isEmpty()) {
            throw new IllegalArgumentException("Path cannot be empty for a file request");
        }
        
        // 移除路径开头的斜杠，避免双斜杠问题
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        
        String url = apiUrl + "/repos/" + owner + "/" + repo + "/contents/" + path;
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        if (ref != null && !ref.isEmpty()) {
            builder.queryParam("ref", ref);
        }

        ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity,
                String.class);
        try {
            return objectMapper.readValue(response.getBody(), RepositoryFile.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing repository file", e);
        }
    }

    @Override
    public List<Commit> getRepositoryCommits(String accessToken, String owner, String repo, String branch) {
        String url = apiUrl + "/repos/" + owner + "/" + repo + "/commits";
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        if (branch != null && !branch.isEmpty()) {
            builder.queryParam("sha", branch);
        }

        ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity,
                String.class);
        try {
            return objectMapper.readValue(response.getBody(), new TypeReference<List<Commit>>() {
            });
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing commits", e);
        }
    }

    @Override
    public Commit createCommit(String accessToken, String owner, String repo, String path, String content,
            String message, String branch) {
        // 确保路径参数格式正确
        if (path == null || path.isEmpty()) {
            throw new IllegalArgumentException("Path cannot be empty for a commit request");
        }
        
        // 移除路径开头的斜杠，避免双斜杠问题
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        
        String url = apiUrl + "/repos/" + owner + "/" + repo + "/contents/" + path;
        HttpHeaders headers = createAuthHeaders(accessToken);
        headers.set("Content-Type", "application/json");

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("message", message);
        requestBody.put("content", Base64.getEncoder().encodeToString(content.getBytes()));
        requestBody.put("branch", branch);

        // Check if file exists to get its SHA for update
        try {
            RepositoryFile existingFile = getRepositoryFile(accessToken, owner, repo, path, branch);
            if (existingFile != null && existingFile.getSha() != null) {
                requestBody.put("sha", existingFile.getSha());
            }
        } catch (Exception e) {
            // File doesn't exist, creating new file
        }

        try {
            String requestBodyJson = objectMapper.writeValueAsString(requestBody);
            HttpEntity<String> entity = new HttpEntity<>(requestBodyJson, headers);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, entity, String.class);

            // Gitee returns commit info in a different format than what we need
            // We'll need to get the latest commit from the branch
            List<Commit> commits = getRepositoryCommits(accessToken, owner, repo, branch);
            if (!commits.isEmpty()) {
                return commits.get(0);
            }
            return null;
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error creating commit", e);
        }
    }

    @Override
    public List<String> getRepositoryBranches(String accessToken, String owner, String repo) {
        String url = apiUrl + "/repos/" + owner + "/" + repo + "/branches";
        HttpHeaders headers = createAuthHeaders(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        try {
            List<Map<String, Object>> branches = objectMapper.readValue(response.getBody(),
                    new TypeReference<List<Map<String, Object>>>() {
                    });
            return branches.stream().map(branch -> (String) branch.get("name")).toList();
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing branches", e);
        }
    }

    @Override
    public String getAccessToken(String code) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Accept", "application/json");
        headers.set("Content-Type", "application/x-www-form-urlencoded");

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("grant_type", "authorization_code");
        map.add("code", code);
        map.add("client_id", clientId);
        map.add("client_secret", clientSecret);
        map.add("redirect_uri", redirectUri);

        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);

        ResponseEntity<String> response = restTemplate.exchange(tokenUrl, HttpMethod.POST, entity, String.class);
        try {
            Map<String, Object> tokenResponse = objectMapper.readValue(response.getBody(),
                    new TypeReference<Map<String, Object>>() {
                    });
            return (String) tokenResponse.get("access_token");
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing access token", e);
        }
    }

    @Override
    public String refreshAccessToken(String refreshToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Accept", "application/json");
        headers.set("Content-Type", "application/x-www-form-urlencoded");

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("grant_type", "refresh_token");
        map.add("refresh_token", refreshToken);
        map.add("client_id", clientId);
        map.add("client_secret", clientSecret);

        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);

        ResponseEntity<String> response = restTemplate.exchange(tokenUrl, HttpMethod.POST, entity, String.class);
        try {
            Map<String, Object> tokenResponse = objectMapper.readValue(response.getBody(),
                    new TypeReference<Map<String, Object>>() {
                    });
            return (String) tokenResponse.get("access_token");
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Error parsing refreshed access token", e);
        }
    }

    private HttpHeaders createAuthHeaders(String accessToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "token " + accessToken);
        return headers;
    }
}